{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](../images/featuretools.png)\n",
    "\n",
    "# Predicting Loan Repayment with Automated Feature Engineering in Featuretools\n",
    "\n",
    "Feature engineering is the process of creating new features (also called predictors or explanatory variables) out of an existing dataset. Traditionally, this process is done by hand using domain knowledge to build new features one at a time. In a previous notebook, we saw that feature engineering is crucial for a data science problem and how the manual approach is time-consuming, tedious, error-prone, and must be re-done for each problem. Automated feature engineering aims to aid the data scientist in this critical process by automatically creating hundreds or thousands of new features from a set of related tables in a fraction of the time as the manual approach. In this notebook, we will apply automated feature engineering to the Home Credit Default Risk loan dataset using [Featuretools, an open-source Python library](https://www.featuretools.com/) for automated feature engineering. \n",
    "\n",
    "This problem is a machine learning competition currently running on Kaggle where the objective is to predict if an applicant will default on a loan given comprehensive data on past loans and applicants. The data is spread across seven different tables making this an ideal problem for automated feature engineering: all of the data must be gathered into a single dataframe for training (and one for testing) with the aim of capturing as much usable information for the prediction problem as possible. As we will see, featuretools can efficiently carry out the tedious process of using all of these tables to make new features with only a few lines of code. Moreover, this code is generally applicable to any data science problem! \n",
    "\n",
    "The general idea of automated feature engineering is picture below:\n",
    "\n",
    "![](../../images/AutomatedFeatureEngineering.png)\n",
    "\n",
    "\n",
    "## Approach \n",
    "\n",
    "In this notebook, we will implement an automated feature engineering approach to the loan repayment problem. While Featuretools allows plenty of options for customization of the library to improve accuracy, we'll focus on a fairly high-level implementation. (Later notebooks, including Engine Life and Retail Spending show some of the additional features of the library).\n",
    "\n",
    "Our approach will be as follows with the background covered as we go:\n",
    "\n",
    "1. Read in the set of related data tables\n",
    "2. Create a featuretools `EntitySet` and add `entities` to it \n",
    "    * Identify correct variable types as required\n",
    "    * Identify indices in data\n",
    "3. Add relationships between `entities`\n",
    "4. Select feature primitives to use to create new features\n",
    "    * Use basic set of primitives\n",
    "    * Examine features that will be created\n",
    "5. Run Deep Feature Synthesis to generate thousands of new features\n",
    "\n",
    "\n",
    "## Problem and Dataset\n",
    "\n",
    "The [Home Credit Default Risk competition](https://www.kaggle.com/c/home-credit-default-risk) currently running on Kaggle is a supervised classification task where the objective is to predict whether or not an applicant for a loan (known as a client) will default on the loan. The data comprises socio-economic indicators for the clients, loan specific financial information, and comprehensive data on previous loans at Home Credit (the institution sponsoring the competition) and other credit agencies. The metric for this competition is Receiver Operating Characteristic Area Under the Curve (ROC AUC) with predictions made in terms of the probability of default. We can evaluate our submissions both through cross-validation on the training data (for which we have the labels) or by submitting our test predictions to Kaggle to see where we place on the public leaderboard (which is calculated with only 10% of the testing data). \n",
    "\n",
    "The Home Credit Default Risk dataset ([available for download here](https://www.kaggle.com/c/home-credit-default-risk/data)) consists of seven related tables of data:\n",
    "\n",
    "* application_train/application_test: the main training/testing data for each client at Home Credit. The information includes both socioeconomic indicators for the client and loan-specific characteristics. Each loan has its own row and is uniquely identified by the feature `SK_ID_CURR`. The training application data comes with the `TARGET` indicating 0: the loan was repaid or 1: the loan was not repaid. \n",
    "* bureau: data concerning client's previous credits from other financial institutions (not Home Credit). Each previous credit has its own row in bureau, but one client in the application data can have multiple previous credits. The previous credits are uniquely identified by the feature `SK_ID_BUREAU`.\n",
    "* bureau_balance: monthly balance data about the credits in bureau. Each row has information for one month about a previous credit and a single previous credit can have multiple rows. This is linked backed to the bureau loan data by `SK_ID_BUREAU` (not unique in this dataframe).\n",
    "* previous_application: previous applications for loans at Home Credit of clients who have loans in the application data. Each client in the application data can have multiple previous loans. Each previous application has one row in this dataframe and is uniquely identified by the feature `SK_ID_PREV`. \n",
    "* POS_CASH_BALANCE: monthly data about previous point of sale or cash loans from the previous loan data. Each row is one month of a previous point of sale or cash loan, and a single previous loan can have many rows. This is linked backed to the previous loan data by `SK_ID_PREV` (not unique in this dataframe).\n",
    "* credit_card_balance: monthly data about previous credit cards loans from the previous loan data. Each row is one month of a credit card balance, and a single credit card can have many rows. This is linked backed to the previous loan data by `SK_ID_PREV` (not unique in this dataframe).\n",
    "* installments_payment: payment history for previous loans at Home Credit. There is one row for every made payment and one row for every missed payment. This is linked backed to the previous loan data by `SK_ID_PREV` (not unique in this dataframe).\n",
    "\n",
    "The image below shows the seven tables and the variables linking them:\n",
    "\n",
    "![](../images/kaggle_home_credit/home_credit_data.png)\n",
    "\n",
    "The variables that tie the tables together will be important to understand when it comes to adding `relationships` between entities. __The only domain knowledge we need for a full Featuretools approach to the problem is the indexes of the tables and the relationships between the tables.__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# pandas and numpy for data manipulation\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "# featuretools for automated feature engineering\n",
    "import featuretools as ft"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Read in Data\n",
    "\n",
    "First we can read in the seven data tables. We also replace the anomalous values previously identified (we did the same process with manual feature engineering). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Read in the datasets and replace the anomalous values\n",
    "app_train = pd.read_csv('input/application_train.csv').replace({365243: np.nan})\n",
    "app_test = pd.read_csv('input/application_test.csv').replace({365243: np.nan})\n",
    "bureau = pd.read_csv('input/bureau.csv').replace({365243: np.nan})\n",
    "bureau_balance = pd.read_csv('input/bureau_balance.csv').replace({365243: np.nan})\n",
    "cash = pd.read_csv('input/POS_CASH_balance.csv').replace({365243: np.nan})\n",
    "credit = pd.read_csv('input/credit_card_balance.csv').replace({365243: np.nan})\n",
    "previous = pd.read_csv('input/previous_application.csv').replace({365243: np.nan})\n",
    "installments = pd.read_csv('input/installments_payments.csv').replace({365243: np.nan})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will join together the training and testing datasets to make sure we build the same features for each set. Later, after the feature matrix is built, we can separate out the two sets. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "app_test['TARGET'] = np.nan\n",
    "\n",
    "# Join together training and testing\n",
    "app = app_train.append(app_test, ignore_index = True, sort = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Several of the indexes are an incorrect data type (floats) so we need to make these all the same (integers) for adding relationships. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "for index in ['SK_ID_CURR', 'SK_ID_PREV', 'SK_ID_BUREAU']:\n",
    "    for dataset in [app, bureau, bureau_balance, cash, credit, previous, installments]:\n",
    "        if index in list(dataset.columns):\n",
    "            dataset[index] = dataset[index].fillna(0).astype(np.int64)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Featuretools Basics\n",
    "\n",
    "[Featuretools](https://docs.featuretools.com/#minute-quick-start) is an open-source Python library for automatically creating features out of a set of related tables using a technique called [Deep Feature Synthesis](http://www.jmaxkanter.com/static/papers/DSAA_DSM_2015.pdf). Automated feature engineering, like many topics in machine learning, is a complex subject built upon a foundation of simpler ideas. By going through these ideas one at a time, we can build up our understanding of Featuretools which will later allow for us to get the most out of it.\n",
    "\n",
    "There are a few concepts that we will cover along the way:\n",
    "\n",
    "* [Entities and EntitySets](https://docs.featuretools.com/loading_data/using_entitysets.html): our tables and a data structure for keeping track of them all\n",
    "* [Relationships between tables](https://docs.featuretools.com/loading_data/using_entitysets.html#adding-a-relationship): how the tables can be related to one another\n",
    "* [Feature primitives](https://docs.featuretools.com/automated_feature_engineering/primitives.html): aggregations and transformations that are stacked to build features\n",
    "* [Deep feature synthesis](https://docs.featuretools.com/automated_feature_engineering/afe.html): the method that uses feature primitives to generate thousands of new features\n",
    "\n",
    "# Entities and Entitysets\n",
    "\n",
    "An entity is simply a table or in Pandas, a `dataframe`. The observations must be in the rows and the features in the columns. An entity in featuretools must have a unique index where none of the elements are duplicated.  Currently, only `app`, `bureau`, and `previous` have unique indices (`SK_ID_CURR`, `SK_ID_BUREAU`, and `SK_ID_PREV` respectively). For the other dataframes, when we create entities from them, we must pass in `make_index = True` and then specify the name of the index. \n",
    "\n",
    "Entities can also have time indices that represent when the information in the row became known. (There are not datetimes in any of the data, but there are relative times, given in months or days, that could be treated as time variables, although we will not use them as time in this notebook).\n",
    "\n",
    "An [EntitySet](https://docs.featuretools.com/loading_data/using_entitysets.html) is a collection of tables and the relationships between them. This can be thought of a data structure with its own methods and attributes. Using an EntitySet allows us to group together multiple tables and will make creating the features much simpler than keeping track of individual tables and relationships. __EntitySets and entities are abstractions that can be applied to any dataset because they do not depend on the underlying data.__\n",
    "\n",
    "First we'll make an empty entityset named clients to keep track of all the data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Entity set with id applications\n",
    "es = ft.EntitySet(id = 'clients')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Variable Types\n",
    "\n",
    "Featuretools will automatically infer the variable types. However, there may be some cases where we need to explicitly tell featuretools the variable type such as when a boolean variable is represented as an integer. Variable types in featuretools can be specified as a dictionary. \n",
    "\n",
    "We will first work with the `app` data to specify the proper variable types. To identify the `Boolean` variables that are recorded as numbers (1.0 or 0.0), we can iterate through the data and find any columns where there are only 2 unique values and the data type is numeric. We can also use the column definitions to find any other data types that should be identified, such as `Ordinal` variables. Identifying the correct variable types is important because Featuretools applies different operations to different data types (just as we do when manual feature engineering)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "import featuretools.variable_types as vtypes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "There are 32 Boolean variables in the application data.\n"
     ]
    }
   ],
   "source": [
    "app_types = {}\n",
    "\n",
    "# Handle the Boolean variables:\n",
    "for col in app:\n",
    "    if (app[col].nunique() == 2) and (app[col].dtype == float):\n",
    "        app_types[col] = vtypes.Boolean\n",
    "\n",
    "# Remove the `TARGET`\n",
    "del app_types['TARGET']\n",
    "\n",
    "print('There are {} Boolean variables in the application data.'.format(len(app_types)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ordinal variables\n",
    "app_types['REGION_RATING_CLIENT'] = vtypes.Ordinal\n",
    "app_types['REGION_RATING_CLIENT_W_CITY'] = vtypes.Ordinal\n",
    "app_types['HOUR_APPR_PROCESS_START'] = vtypes.Ordinal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `previous` table is the only other `entity` that has features which should be recorded as Boolean. Correctly identifying the type of column will prevent featuretools from making irrelevant features such as the mean or max of a `Boolean`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "There are 2 Boolean variables in the previous data.\n"
     ]
    }
   ],
   "source": [
    "previous_types = {}\n",
    "\n",
    "# Handle the Boolean variables:\n",
    "for col in previous:\n",
    "    if (previous[col].nunique() == 2) and (previous[col].dtype == float):\n",
    "        previous_types[col] = vtypes.Boolean\n",
    "\n",
    "print('There are {} Boolean variables in the previous data.'.format(len(previous_types)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition to identifying Boolean variables, we want to make sure featuretools does not create nonsense features such as statistical aggregations (mean, max, etc.) of ids. The `credit`, `cash`, and `installments` data all have the `SK_ID_CURR` variable. However, we do not actually need this variable in these dataframes because we link them to `app` through the `previous` dataframe with the `SK_ID_PREV` variable. \n",
    "\n",
    "We don't want to make features from `SK_ID_CURR` since it is an arbitrary id and should have no predictive power. \n",
    "Our options to handle these variables is either to tell featuretools to ignore them, or to drop the features before including them in the entityset. We will take the latter approach."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "installments = installments.drop(columns = ['SK_ID_CURR'])\n",
    "credit = credit.drop(columns = ['SK_ID_CURR'])\n",
    "cash = cash.drop(columns = ['SK_ID_CURR'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Adding Entities\n",
    "\n",
    "Now we define each entity, or table of data, and add it to the `EntitySet`. We need to pass in an index if the table has one or `make_index = True` if not. In the cases where we need to make an index, we must supply a name for the index. We also need to pass in the dictionary of variable types if there are any specific variables we should identify. The following code adds all seven tables to the `EntitySet`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Entities with a unique index\n",
    "es = es.entity_from_dataframe(entity_id = 'app', dataframe = app, index = 'SK_ID_CURR',\n",
    "                              variable_types = app_types)\n",
    "\n",
    "es = es.entity_from_dataframe(entity_id = 'bureau', dataframe = bureau, index = 'SK_ID_BUREAU')\n",
    "\n",
    "es = es.entity_from_dataframe(entity_id = 'previous', dataframe = previous, index = 'SK_ID_PREV',\n",
    "                              variable_types = previous_types)\n",
    "\n",
    "# Entities that do not have a unique index\n",
    "es = es.entity_from_dataframe(entity_id = 'bureau_balance', dataframe = bureau_balance, \n",
    "                              make_index = True, index = 'bureaubalance_index')\n",
    "\n",
    "es = es.entity_from_dataframe(entity_id = 'cash', dataframe = cash, \n",
    "                              make_index = True, index = 'cash_index')\n",
    "\n",
    "es = es.entity_from_dataframe(entity_id = 'installments', dataframe = installments,\n",
    "                              make_index = True, index = 'installments_index')\n",
    "\n",
    "es = es.entity_from_dataframe(entity_id = 'credit', dataframe = credit,\n",
    "                              make_index = True, index = 'credit_index')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Entityset: clients\n",
       "  Entities:\n",
       "    app [Rows: 356255, Columns: 122]\n",
       "    bureau [Rows: 1716428, Columns: 17]\n",
       "    previous [Rows: 1670214, Columns: 37]\n",
       "    bureau_balance [Rows: 27299925, Columns: 4]\n",
       "    cash [Rows: 10001358, Columns: 8]\n",
       "    installments [Rows: 13605401, Columns: 8]\n",
       "    credit [Rows: 3840312, Columns: 23]\n",
       "  Relationships:\n",
       "    No relationships"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display entityset so far\n",
    "es"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `EntitySet` allows us to group together all of our tables as one data structure. This is much easier than manipulating the tables one at a time (as we have to do in manual feature engineering)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Relationships\n",
    "\n",
    "Relationships are a fundamental concept not only in featuretools, but in any relational database. The most common type of relationship is one-to-many. The best way to think of a one-to-many relationship is with the analogy of parent-to-child. A parent is a single individual, but can have mutliple children. In the context of tables, a parent table will have one row (observation) for every individual while a child table can have many observations for each parent.  In a _parent table_, each individual has a single row and is uniquely identified by an index (also called a key). Each individual in the parent table can have multiple rows in the _child table_. Things get a little more complicated because children tables can have children of their own, making these grandchildren of the original parent. \n",
    "\n",
    "As an example of a parent-to-child relationship, the `app` dataframe has one row for each client (identified by `SK_ID_CURR`) while the `bureau` dataframe has multiple previous loans for each client. Therefore, the `bureau` dataframe is the child of the `app` dataframe. The `bureau` dataframe in turn is the parent of `bureau_balance` because each loan has one row in `bureau` (identified by `SK_ID_BUREAU`) but multiple monthly records in `bureau_balance`. When we do manual feature engineering, keeping track of all these relationships is a massive time investment (and a potential source of error), but we can add these relationships to our `EntitySet` and let featuretools worry about keeping the tables straight!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Parent: app, Parent Variable of bureau: SK_ID_CURR\n",
      "\n",
      "    SK_ID_CURR  TARGET  TOTALAREA_MODE WALLSMATERIAL_MODE\n",
      "0      100002     1.0          0.0149       Stone, brick\n",
      "1      100003     0.0          0.0714              Block\n",
      "2      100004     0.0             NaN                NaN\n",
      "3      100006     0.0             NaN                NaN\n",
      "4      100007     0.0             NaN                NaN\n",
      "\n",
      "Child: bureau, Child Variable of app: SK_ID_CURR\n",
      "\n",
      "    SK_ID_CURR  SK_ID_BUREAU CREDIT_ACTIVE CREDIT_CURRENCY  DAYS_CREDIT\n",
      "0      215354       5714462        Closed      currency 1       -497.0\n",
      "1      215354       5714463        Active      currency 1       -208.0\n",
      "2      215354       5714464        Active      currency 1       -203.0\n",
      "3      215354       5714465        Active      currency 1       -203.0\n",
      "4      215354       5714466        Active      currency 1       -629.0\n"
     ]
    }
   ],
   "source": [
    "print('Parent: app, Parent Variable of bureau: SK_ID_CURR\\n\\n', app.iloc[:, 111:115].head())\n",
    "print('\\nChild: bureau, Child Variable of app: SK_ID_CURR\\n\\n', bureau.iloc[:, :5].head())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `SK_ID_CURR` 215354 has one row in the parent table and multiple rows in the child. \n",
    "\n",
    "Two tables are linked via a shared variable. The `app` and `bureau` dataframe are linked by the `SK_ID_CURR` variable while the `bureau` and `bureau_balance` dataframes are linked with the `SK_ID_BUREAU`. The linking variable is called the `parent` variable in the parent table and the `child` variable in the child table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Parent: bureau, Parent Variable of bureau_balance: SK_ID_BUREAU\n",
      "\n",
      "    SK_ID_CURR  SK_ID_BUREAU CREDIT_ACTIVE CREDIT_CURRENCY  DAYS_CREDIT\n",
      "0      215354       5714462        Closed      currency 1       -497.0\n",
      "1      215354       5714463        Active      currency 1       -208.0\n",
      "2      215354       5714464        Active      currency 1       -203.0\n",
      "3      215354       5714465        Active      currency 1       -203.0\n",
      "4      215354       5714466        Active      currency 1       -629.0\n",
      "\n",
      "Child: bureau_balance, Child Variable of bureau: SK_ID_BUREAU\n",
      "\n",
      "    bureaubalance_index  SK_ID_BUREAU  MONTHS_BALANCE STATUS\n",
      "0                    0       5715448               0      C\n",
      "1                    1       5715448              -1      C\n",
      "2                    2       5715448              -2      C\n",
      "3                    3       5715448              -3      C\n",
      "4                    4       5715448              -4      C\n"
     ]
    }
   ],
   "source": [
    "print('Parent: bureau, Parent Variable of bureau_balance: SK_ID_BUREAU\\n\\n', bureau.iloc[:, :5].head())\n",
    "print('\\nChild: bureau_balance, Child Variable of bureau: SK_ID_BUREAU\\n\\n', bureau_balance.head())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Traditionally, we use the relationships between parents and children to aggregate data by grouping together all the children for a single parent and calculating statistics. For example, we might group together all the loans for a single client and calculate the average loan amount. This is straightforward, but can grow extremely tedious when we want to make hundreds of these features. Doing so one at a time is extremely inefficient especially because we end up re-writing much of the code over and over again and this code cannot be used for any different problem! \n",
    "\n",
    "Things get even worse when we have to aggregate the grandchildren because we have to use two steps: first aggregate at the parent level, and then at the grandparent level. Soon we will see that featuretools can do this work automatically for us, generating thousands of features from __all__ of the data tables. When we did this manually it took about 15 minutes per feature (as we saw in the manual feature engineering notebook) so featuretools potentially saves us hundreds of hours.\n",
    "\n",
    "### Adding Relationships\n",
    "\n",
    "Defining the relationships is straightforward using the diagram for the data tables. For each relationship, we need to first specify the parent variable and then the child variable. Altogether, there are a total of 6 relationships between the tables (counting the training and testing relationships as one). Below we specify these relationships and then add them to the EntitySet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Relationship between app_train and bureau\n",
    "r_app_bureau = ft.Relationship(es['app']['SK_ID_CURR'], es['bureau']['SK_ID_CURR'])\n",
    "\n",
    "# Relationship between bureau and bureau balance\n",
    "r_bureau_balance = ft.Relationship(es['bureau']['SK_ID_BUREAU'], es['bureau_balance']['SK_ID_BUREAU'])\n",
    "\n",
    "# Relationship between current app and previous apps\n",
    "r_app_previous = ft.Relationship(es['app']['SK_ID_CURR'], es['previous']['SK_ID_CURR'])\n",
    "\n",
    "# Relationships between previous apps and cash, installments, and credit\n",
    "r_previous_cash = ft.Relationship(es['previous']['SK_ID_PREV'], es['cash']['SK_ID_PREV'])\n",
    "r_previous_installments = ft.Relationship(es['previous']['SK_ID_PREV'], es['installments']['SK_ID_PREV'])\n",
    "r_previous_credit = ft.Relationship(es['previous']['SK_ID_PREV'], es['credit']['SK_ID_PREV'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Entityset: clients\n",
       "  Entities:\n",
       "    app [Rows: 356255, Columns: 122]\n",
       "    bureau [Rows: 1716428, Columns: 17]\n",
       "    previous [Rows: 1670214, Columns: 37]\n",
       "    bureau_balance [Rows: 27299925, Columns: 4]\n",
       "    cash [Rows: 10001358, Columns: 8]\n",
       "    installments [Rows: 13605401, Columns: 8]\n",
       "    credit [Rows: 3840312, Columns: 23]\n",
       "  Relationships:\n",
       "    bureau.SK_ID_CURR -> app.SK_ID_CURR\n",
       "    bureau_balance.SK_ID_BUREAU -> bureau.SK_ID_BUREAU\n",
       "    previous.SK_ID_CURR -> app.SK_ID_CURR\n",
       "    cash.SK_ID_PREV -> previous.SK_ID_PREV\n",
       "    installments.SK_ID_PREV -> previous.SK_ID_PREV\n",
       "    credit.SK_ID_PREV -> previous.SK_ID_PREV"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Add in the defined relationships\n",
    "es = es.add_relationships([r_app_bureau, r_bureau_balance, r_app_previous,\n",
    "                           r_previous_cash, r_previous_installments, r_previous_credit])\n",
    "# Print out the EntitySet\n",
    "es"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, we can see the benefits of using an `EntitySet` that is able to track all of the relationships for us. This allows us to work at a higher level of abstraction, thinking about the entire dataset rather than each individual table, greatly increasing our efficiency."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Slightly advanced note__: we need to be careful to not create a [diamond graph](https://en.wikipedia.org/wiki/Diamond_graph) where there are multiple paths from a parent to a child. If we directly link `app` and `cash` via `SK_ID_CURR`; `previous` and `cash` via `SK_ID_PREV`; and `app` and `previous` via `SK_ID_CURR`, then we have created two paths from `app` to `cash`. This results in ambiguity, so the approach we have to take instead is to link `app` to `cash` through `previous`. We establish a relationship between `previous` (the parent) and `cash` (the child) using `SK_ID_PREV`. Then we establish a relationship between `app` (the parent) and `previous` (now the child) using `SK_ID_CURR`. Then featuretools will be able to create features on `app` derived from both `previous` and `cash` by stacking multiple primitives. \n",
    "\n",
    "If this doesn't make too much sense, then just remember to only include one path from a parent to any descendents. For example, link a grandparent to a grandchild through the parent instead of directly through a shared variable."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All entities in the entity can be linked through these relationships. In theory this allows us to calculate features for any of the entities, but in practice, we will only calculate features for the `app` dataframe since that will be used for training/testing. The end outcome will be a dataframe that has one row for each client in `app` with thousands of features for each individual. \n",
    "\n",
    "We are almost to the point where we can start creating thousands of features but we still have a few foundational topics to understand. The next building block to cover is feature primitives."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualize EntitySet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.40.1 (20161225.0304)\n",
       " -->\n",
       "<!-- Title: clients Pages: 1 -->\n",
       "<svg width=\"1123pt\" height=\"2906pt\"\n",
       " viewBox=\"0.00 0.00 1123.00 2906.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 2902)\">\n",
       "<title>clients</title>\n",
       "<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-2902 1119,-2902 1119,4 -4,4\"/>\n",
       "<!-- app -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>app</title>\n",
       "<polygon fill=\"none\" stroke=\"#000000\" points=\"213.5,-.5 213.5,-1861.5 537.5,-1861.5 537.5,-.5 213.5,-.5\"/>\n",
       "<text text-anchor=\"middle\" x=\"375.5\" y=\"-1846.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">app</text>\n",
       "<polyline fill=\"none\" stroke=\"#000000\" points=\"213.5,-1838.5 537.5,-1838.5 \"/>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1823.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_CURR : index</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1808.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_ANNUITY : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1793.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1778.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_GOODS_PRICE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1763.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_INCOME_TOTAL : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1748.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_REQ_CREDIT_BUREAU_DAY : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1733.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_REQ_CREDIT_BUREAU_HOUR : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1718.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_REQ_CREDIT_BUREAU_MON : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1703.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_REQ_CREDIT_BUREAU_QRT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1688.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_REQ_CREDIT_BUREAU_WEEK : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1673.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_REQ_CREDIT_BUREAU_YEAR : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1658.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">APARTMENTS_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1643.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">APARTMENTS_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1628.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">APARTMENTS_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1613.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">BASEMENTAREA_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1598.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">BASEMENTAREA_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1583.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">BASEMENTAREA_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1568.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_CHILDREN : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1553.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_FAM_MEMBERS : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1538.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CODE_GENDER : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1523.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">COMMONAREA_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1508.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">COMMONAREA_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1493.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">COMMONAREA_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1478.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_BIRTH : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1463.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_EMPLOYED : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1448.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_ID_PUBLISH : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1433.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_LAST_PHONE_CHANGE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1418.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_REGISTRATION : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1403.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DEF_30_CNT_SOCIAL_CIRCLE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1388.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DEF_60_CNT_SOCIAL_CIRCLE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1373.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">ELEVATORS_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1358.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">ELEVATORS_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1343.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">ELEVATORS_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1328.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">EMERGENCYSTATE_MODE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1313.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">ENTRANCES_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1298.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">ENTRANCES_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1283.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">ENTRANCES_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1268.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">EXT_SOURCE_1 : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1253.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">EXT_SOURCE_2 : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1238.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">EXT_SOURCE_3 : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1223.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_OWN_CAR : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1208.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_OWN_REALTY : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1193.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLOORSMAX_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1178.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLOORSMAX_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1163.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLOORSMAX_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1148.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLOORSMIN_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1133.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLOORSMIN_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1118.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLOORSMIN_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1103.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FONDKAPREMONT_MODE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1088.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">HOUSETYPE_MODE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1073.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LANDAREA_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1058.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LANDAREA_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1043.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LANDAREA_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1028.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVINGAPARTMENTS_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-1013.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVINGAPARTMENTS_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-998.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVINGAPARTMENTS_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-983.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVINGAREA_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-968.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVINGAREA_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-953.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVINGAREA_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-938.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_CONTRACT_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-923.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_EDUCATION_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-908.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_FAMILY_STATUS : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-893.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_HOUSING_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-878.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_INCOME_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-863.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_TYPE_SUITE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-848.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NONLIVINGAPARTMENTS_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-833.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NONLIVINGAPARTMENTS_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-818.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NONLIVINGAPARTMENTS_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-803.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NONLIVINGAREA_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-788.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NONLIVINGAREA_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-773.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NONLIVINGAREA_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-758.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">OBS_30_CNT_SOCIAL_CIRCLE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-743.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">OBS_60_CNT_SOCIAL_CIRCLE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-728.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">OCCUPATION_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-713.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">ORGANIZATION_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-698.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">OWN_CAR_AGE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-683.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">REGION_POPULATION_RELATIVE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-668.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">TARGET : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-653.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">TOTALAREA_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-638.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">WALLSMATERIAL_MODE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-623.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">WEEKDAY_APPR_PROCESS_START : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-608.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">YEARS_BEGINEXPLUATATION_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-593.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">YEARS_BEGINEXPLUATATION_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-578.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">YEARS_BEGINEXPLUATATION_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-563.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">YEARS_BUILD_AVG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-548.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">YEARS_BUILD_MEDI : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-533.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">YEARS_BUILD_MODE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-518.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_CONT_MOBILE : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-503.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_10 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-488.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_11 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-473.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_12 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-458.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_13 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-443.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_14 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-428.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_15 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-413.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_16 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-398.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_17 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-383.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_18 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-368.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_19 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-353.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_2 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-338.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_20 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-323.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_21 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-308.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_3 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-293.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_4 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-278.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_5 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-263.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_6 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-248.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_7 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-233.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_8 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-218.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_DOCUMENT_9 : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-203.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_EMAIL : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-188.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_EMP_PHONE : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-173.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_MOBIL : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_PHONE : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-143.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_WORK_PHONE : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-128.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVE_CITY_NOT_WORK_CITY : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-113.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LIVE_REGION_NOT_WORK_REGION : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-98.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">REG_CITY_NOT_LIVE_CITY : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-83.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">REG_CITY_NOT_WORK_CITY : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-68.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">REG_REGION_NOT_LIVE_REGION : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-53.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">REG_REGION_NOT_WORK_REGION : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-38.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">REGION_RATING_CLIENT : ordinal</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-23.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">REGION_RATING_CLIENT_W_CITY : ordinal</text>\n",
       "<text text-anchor=\"start\" x=\"221.5\" y=\"-8.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">HOUR_APPR_PROCESS_START : ordinal</text>\n",
       "</g>\n",
       "<!-- bureau -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>bureau</title>\n",
       "<polygon fill=\"none\" stroke=\"#000000\" points=\"79,-2048.5 79,-2334.5 354,-2334.5 354,-2048.5 79,-2048.5\"/>\n",
       "<text text-anchor=\"middle\" x=\"216.5\" y=\"-2319.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">bureau</text>\n",
       "<polyline fill=\"none\" stroke=\"#000000\" points=\"79,-2311.5 354,-2311.5 \"/>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2296.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_BUREAU : index</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2281.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_CURR : id</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2266.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CREDIT_ACTIVE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2251.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CREDIT_CURRENCY : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2236.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_CREDIT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2221.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CREDIT_DAY_OVERDUE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2206.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_CREDIT_ENDDATE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2191.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_ENDDATE_FACT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2176.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT_MAX_OVERDUE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2161.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_CREDIT_PROLONG : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2146.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT_SUM : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2131.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT_SUM_DEBT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2116.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT_SUM_LIMIT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2101.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT_SUM_OVERDUE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2086.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CREDIT_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2071.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_CREDIT_UPDATE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"87\" y=\"-2056.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_ANNUITY : numeric</text>\n",
       "</g>\n",
       "<!-- bureau&#45;&gt;app -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>bureau&#45;&gt;app</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M283.75,-2048.438C283.75,-2048.438 283.75,-1871.6835 283.75,-1871.6835\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"287.2501,-1871.6835 283.75,-1861.6835 280.2501,-1871.6835 287.2501,-1871.6835\"/>\n",
       "<text text-anchor=\"middle\" x=\"241.25\" y=\"-1963.8607\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_CURR</text>\n",
       "</g>\n",
       "<!-- previous -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>previous</title>\n",
       "<polygon fill=\"none\" stroke=\"#000000\" points=\"429.5,-1898.5 429.5,-2484.5 753.5,-2484.5 753.5,-1898.5 429.5,-1898.5\"/>\n",
       "<text text-anchor=\"middle\" x=\"591.5\" y=\"-2469.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">previous</text>\n",
       "<polyline fill=\"none\" stroke=\"#000000\" points=\"429.5,-2461.5 753.5,-2461.5 \"/>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2446.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_PREV : index</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2431.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_CURR : id</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2416.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_CONTRACT_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2401.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_ANNUITY : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2386.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_APPLICATION : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2371.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2356.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_DOWN_PAYMENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2341.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_GOODS_PRICE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2326.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">WEEKDAY_APPR_PROCESS_START : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2311.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">HOUR_APPR_PROCESS_START : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2296.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">FLAG_LAST_APPL_PER_CONTRACT : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2281.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">RATE_DOWN_PAYMENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2266.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">RATE_INTEREST_PRIMARY : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2251.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">RATE_INTEREST_PRIVILEGED : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2236.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_CASH_LOAN_PURPOSE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2221.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_CONTRACT_STATUS : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2206.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_DECISION : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2191.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_PAYMENT_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2176.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CODE_REJECT_REASON : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2161.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_TYPE_SUITE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2146.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_CLIENT_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2131.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_GOODS_CATEGORY : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2116.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_PORTFOLIO : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2101.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_PRODUCT_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2086.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CHANNEL_TYPE : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2071.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SELLERPLACE_AREA : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2056.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_SELLER_INDUSTRY : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2041.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_PAYMENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2026.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_YIELD_GROUP : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-2011.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">PRODUCT_COMBINATION : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-1996.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_FIRST_DRAWING : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-1981.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_FIRST_DUE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-1966.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_LAST_DUE_1ST_VERSION : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-1951.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_LAST_DUE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-1936.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_TERMINATION : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-1921.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NFLAG_LAST_APPL_IN_DAY : boolean</text>\n",
       "<text text-anchor=\"start\" x=\"437.5\" y=\"-1906.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NFLAG_INSURED_ON_APPROVAL : boolean</text>\n",
       "</g>\n",
       "<!-- previous&#45;&gt;app -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>previous&#45;&gt;app</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M483.5,-1898.354C483.5,-1898.354 483.5,-1871.6829 483.5,-1871.6829\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"487.0001,-1871.6829 483.5,-1861.6829 480.0001,-1871.6829 487.0001,-1871.6829\"/>\n",
       "<text text-anchor=\"middle\" x=\"441\" y=\"-1873.8184\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_CURR</text>\n",
       "</g>\n",
       "<!-- bureau_balance -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>bureau_balance</title>\n",
       "<polygon fill=\"none\" stroke=\"#000000\" points=\"0,-2664 0,-2755 205,-2755 205,-2664 0,-2664\"/>\n",
       "<text text-anchor=\"middle\" x=\"102.5\" y=\"-2739.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">bureau_balance</text>\n",
       "<polyline fill=\"none\" stroke=\"#000000\" points=\"0,-2732 205,-2732 \"/>\n",
       "<text text-anchor=\"start\" x=\"8\" y=\"-2716.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">bureaubalance_index : index</text>\n",
       "<text text-anchor=\"start\" x=\"8\" y=\"-2701.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_BUREAU : id</text>\n",
       "<text text-anchor=\"start\" x=\"8\" y=\"-2686.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">MONTHS_BALANCE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"8\" y=\"-2671.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">STATUS : categorical</text>\n",
       "</g>\n",
       "<!-- bureau_balance&#45;&gt;bureau -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>bureau_balance&#45;&gt;bureau</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M142,-2663.9849C142,-2663.9849 142,-2344.6473 142,-2344.6473\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"145.5001,-2344.6473 142,-2334.6473 138.5001,-2344.6474 145.5001,-2344.6473\"/>\n",
       "<text text-anchor=\"middle\" x=\"89.5\" y=\"-2508.1161\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_BUREAU</text>\n",
       "</g>\n",
       "<!-- cash -->\n",
       "<g id=\"node5\" class=\"node\">\n",
       "<title>cash</title>\n",
       "<polygon fill=\"none\" stroke=\"#000000\" points=\"223.5,-2634 223.5,-2785 495.5,-2785 495.5,-2634 223.5,-2634\"/>\n",
       "<text text-anchor=\"middle\" x=\"359.5\" y=\"-2769.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">cash</text>\n",
       "<polyline fill=\"none\" stroke=\"#000000\" points=\"223.5,-2762 495.5,-2762 \"/>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2746.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">cash_index : index</text>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2731.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_PREV : id</text>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2716.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">MONTHS_BALANCE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2701.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_INSTALMENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2686.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_INSTALMENT_FUTURE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2671.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_CONTRACT_STATUS : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2656.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_DPD : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"231.5\" y=\"-2641.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_DPD_DEF : numeric</text>\n",
       "</g>\n",
       "<!-- cash&#45;&gt;previous -->\n",
       "<g id=\"edge4\" class=\"edge\">\n",
       "<title>cash&#45;&gt;previous</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M462.5,-2633.9012C462.5,-2633.9012 462.5,-2494.5048 462.5,-2494.5048\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"466.0001,-2494.5048 462.5,-2484.5048 459.0001,-2494.5049 466.0001,-2494.5048\"/>\n",
       "<text text-anchor=\"middle\" x=\"421\" y=\"-2568.003\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_PREV</text>\n",
       "</g>\n",
       "<!-- installments -->\n",
       "<g id=\"node6\" class=\"node\">\n",
       "<title>installments</title>\n",
       "<polygon fill=\"none\" stroke=\"#000000\" points=\"513.5,-2634 513.5,-2785 783.5,-2785 783.5,-2634 513.5,-2634\"/>\n",
       "<text text-anchor=\"middle\" x=\"648.5\" y=\"-2769.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">installments</text>\n",
       "<polyline fill=\"none\" stroke=\"#000000\" points=\"513.5,-2762 783.5,-2762 \"/>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2746.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">installments_index : index</text>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2731.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_PREV : id</text>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2716.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NUM_INSTALMENT_VERSION : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2701.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NUM_INSTALMENT_NUMBER : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2686.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_INSTALMENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2671.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">DAYS_ENTRY_PAYMENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2656.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_INSTALMENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"521.5\" y=\"-2641.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_PAYMENT : numeric</text>\n",
       "</g>\n",
       "<!-- installments&#45;&gt;previous -->\n",
       "<g id=\"edge5\" class=\"edge\">\n",
       "<title>installments&#45;&gt;previous</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M593.5,-2633.9012C593.5,-2633.9012 593.5,-2494.5048 593.5,-2494.5048\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"597.0001,-2494.5048 593.5,-2484.5048 590.0001,-2494.5049 597.0001,-2494.5048\"/>\n",
       "<text text-anchor=\"middle\" x=\"552\" y=\"-2568.003\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_PREV</text>\n",
       "</g>\n",
       "<!-- credit -->\n",
       "<g id=\"node7\" class=\"node\">\n",
       "<title>credit</title>\n",
       "<polygon fill=\"none\" stroke=\"#000000\" points=\"802,-2521.5 802,-2897.5 1115,-2897.5 1115,-2521.5 802,-2521.5\"/>\n",
       "<text text-anchor=\"middle\" x=\"958.5\" y=\"-2882.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">credit</text>\n",
       "<polyline fill=\"none\" stroke=\"#000000\" points=\"802,-2874.5 1115,-2874.5 \"/>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2859.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">credit_index : index</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2844.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_PREV : id</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2829.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">MONTHS_BALANCE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2814.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_BALANCE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2799.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_CREDIT_LIMIT_ACTUAL : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2784.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_DRAWINGS_ATM_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2769.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_DRAWINGS_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2754.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_DRAWINGS_OTHER_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2739.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_DRAWINGS_POS_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2724.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_INST_MIN_REGULARITY : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2709.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_PAYMENT_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2694.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_PAYMENT_TOTAL_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2679.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_RECEIVABLE_PRINCIPAL : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2664.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_RECIVABLE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2649.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">AMT_TOTAL_RECEIVABLE : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2634.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_DRAWINGS_ATM_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2619.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_DRAWINGS_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2604.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_DRAWINGS_OTHER_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2589.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_DRAWINGS_POS_CURRENT : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2574.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">CNT_INSTALMENT_MATURE_CUM : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2559.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">NAME_CONTRACT_STATUS : categorical</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2544.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_DPD : numeric</text>\n",
       "<text text-anchor=\"start\" x=\"810\" y=\"-2529.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_DPD_DEF : numeric</text>\n",
       "</g>\n",
       "<!-- credit&#45;&gt;previous -->\n",
       "<g id=\"edge6\" class=\"edge\">\n",
       "<title>credit&#45;&gt;previous</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M801.8265,-2577C735.6013,-2577 673.5,-2577 673.5,-2577 673.5,-2577 673.5,-2494.6631 673.5,-2494.6631\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"677.0001,-2494.6631 673.5,-2484.6631 670.0001,-2494.6632 677.0001,-2494.6631\"/>\n",
       "<text text-anchor=\"middle\" x=\"654.9948\" y=\"-2580.8\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">SK_ID_PREV</text>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.dot.Digraph at 0x12caef940>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "es.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Feature Primitives\n",
    "\n",
    "A [feature primitive](https://docs.featuretools.com/automated_feature_engineering/primitives.html) is an operation applied to a table or a set of tables to create a feature. These represent simple calculations, many of which we already use in manual feature engineering, that can be stacked on top of each other to create complex deep features. Feature primitives fall into two categories:\n",
    "\n",
    "* __Aggregation__: function that groups together children for each parent and calculates a statistic such as mean, min, max, or standard deviation across the children. An example is the maximum previous loan amount for each client. An aggregation covers multiple tables using relationships between tables.\n",
    "* __Transformation__: an operation applied to one or more columns in a single table. An example would be taking the absolute value of a column, or finding the difference between two columns in one table.\n",
    "\n",
    "A list of the available features primitives in featuretools can be viewed below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>name</th>\n",
       "      <th>type</th>\n",
       "      <th>description</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>mode</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>num_unique</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>skew</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>min</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>max</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>mean</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>avg_time_between</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>median</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>std</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>count</td>\n",
       "      <td>aggregation</td>\n",
       "      <td>Sums elements of a numeric or boolean feature.</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               name         type  \\\n",
       "0              mode  aggregation   \n",
       "1        num_unique  aggregation   \n",
       "2              skew  aggregation   \n",
       "3               min  aggregation   \n",
       "4               max  aggregation   \n",
       "5              mean  aggregation   \n",
       "6  avg_time_between  aggregation   \n",
       "7            median  aggregation   \n",
       "8               std  aggregation   \n",
       "9             count  aggregation   \n",
       "\n",
       "                                      description  \n",
       "0  Sums elements of a numeric or boolean feature.  \n",
       "1  Sums elements of a numeric or boolean feature.  \n",
       "2  Sums elements of a numeric or boolean feature.  \n",
       "3  Sums elements of a numeric or boolean feature.  \n",
       "4  Sums elements of a numeric or boolean feature.  \n",
       "5  Sums elements of a numeric or boolean feature.  \n",
       "6  Sums elements of a numeric or boolean feature.  \n",
       "7  Sums elements of a numeric or boolean feature.  \n",
       "8  Sums elements of a numeric or boolean feature.  \n",
       "9  Sums elements of a numeric or boolean feature.  "
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# List the primitives in a dataframe\n",
    "primitives = ft.list_primitives()\n",
    "pd.options.display.max_colwidth = 100\n",
    "\n",
    "primitives[primitives['type'] == 'aggregation'].head(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>name</th>\n",
       "      <th>type</th>\n",
       "      <th>description</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>and</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>percentile</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>subtract_numeric</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>year</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>greater_than_equal_to_scalar</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>modulo_numeric_scalar</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>negate</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>add_numeric</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>week</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>greater_than_scalar</td>\n",
       "      <td>transform</td>\n",
       "      <td></td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                            name       type description\n",
       "19                           and  transform            \n",
       "20                    percentile  transform            \n",
       "21              subtract_numeric  transform            \n",
       "22                          year  transform            \n",
       "23  greater_than_equal_to_scalar  transform            \n",
       "24         modulo_numeric_scalar  transform            \n",
       "25                        negate  transform            \n",
       "26                   add_numeric  transform            \n",
       "27                          week  transform            \n",
       "28           greater_than_scalar  transform            "
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "primitives[primitives['type'] == 'transform'].head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deep Feature Synthesis\n",
    "\n",
    "[Deep Feature Synthesis (DFS)](https://www.featurelabs.com/blog/deep-feature-synthesis/) is the method Featuretools uses to make new features. DFS stacks feature primitives to form features with a \"depth\" equal to the number of primitives. For example, if we take the maximum value of a client's previous loans (say `MAX(previous.loan_amount)`), that is a \"deep feature\" with a depth of 1. To create a feature with a depth of two, we could stack primitives by taking the maximum value of a client's average monthly payments per previous loan (such as `MAX(previous(MEAN(installments.payment)))`). In manual feature engineering, this would require two separate groupings and aggregations and took more than 15 minutes to write the code per feature. \n",
    "\n",
    "Deep Feature Synthesis is an extremely powerful method that allows us to overcome our human limitations on time and creativity by building features that we would never be able to think of on our own (or would not have the patience to implement). Furthermore, DFS is applicable to any dataset with only very minor changes in syntax. In feature engineering, we generally apply the same functions to multiple datasets, but when we do it by hand, we have to re-write the code because it is problem-specific. Featuretools code can be applied to any dataset because it is written at a higher level of abstraction.\n",
    "\n",
    "The [original paper on automated feature engineering using Deep Feature Synthesis](https://dai.lids.mit.edu/wp-content/uploads/2017/10/DSAA_DSM_2015.pdf) is worth a read if you want to understand the concepts at a deeper level.\n",
    "\n",
    "To perform DFS in featuretools, we use the `dfs`  function passing it an `entityset`, the `target_entity` (where we want to make the features), the `agg_primitives` to use, the `trans_primitives` to use, the `max_depth` of the features, and a number of other arguments depending on our use case. There are also options for multi-processing with `njobs` and the information that is printed out with `verbose`. \n",
    "\n",
    "One other important argument is __`features_only`__. If we set this to `True`, `dfs` will only make the feature names and not calculate the actual values of the features (called the feature matrix). This is useful when we want to inspect the feature that will be created and we can also save the features to use with a different dataset (for example when we have training and testing data)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deep Feature Synthesis with Default Primitives\n",
    "\n",
    "Without using any domain knowledge we can make thousands of features by using the default primitives in featuretools. This first call will use the default aggregation and transformation primitives,  a max depth of 2, and calculate primitives for the `app` entity. We will only generate the features themselves (the names and not the values) which we can save and inspect."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Built 1575 features\n"
     ]
    }
   ],
   "source": [
    "# Default primitives from featuretools\n",
    "default_agg_primitives =  [\"sum\", \"std\", \"max\", \"skew\", \"min\", \"mean\", \"count\", \"percent_true\", \"num_unique\", \"mode\"]\n",
    "default_trans_primitives =  [\"day\", \"year\", \"month\", \"weekday\", \"haversine\", \"num_words\", \"num_characters\"]\n",
    "\n",
    "# DFS with specified primitives\n",
    "feature_names = ft.dfs(entityset = es, target_entity = 'app',\n",
    "                       trans_primitives = default_trans_primitives,\n",
    "                       agg_primitives=default_agg_primitives, \n",
    "                       where_primitives = [], seed_features = [],\n",
    "                       max_depth = 2, n_jobs = -1, verbose = 1,\n",
    "                       features_only=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Even a basic call to deep feature synthesis gives us over 1500 features to work with. Granted, not all of these will be important, but this still represents hundreds of hours that we saved. Moreover, `dfs` might be able to find important features that we would never have thought of in the first place. \n",
    "\n",
    "We can look at the some of the feature names:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<Feature: MIN(bureau.MAX(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MIN(bureau.SKEW(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MIN(bureau.MEAN(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MIN(bureau.COUNT(bureau_balance))>,\n",
       " <Feature: MIN(bureau.NUM_UNIQUE(bureau_balance.STATUS))>,\n",
       " <Feature: MEAN(bureau.SUM(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MEAN(bureau.STD(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MEAN(bureau.MAX(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MEAN(bureau.SKEW(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MEAN(bureau.MIN(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MEAN(bureau.MEAN(bureau_balance.MONTHS_BALANCE))>,\n",
       " <Feature: MEAN(bureau.COUNT(bureau_balance))>,\n",
       " <Feature: MEAN(bureau.NUM_UNIQUE(bureau_balance.STATUS))>,\n",
       " <Feature: NUM_UNIQUE(bureau.MODE(bureau_balance.STATUS))>,\n",
       " <Feature: MODE(bureau.MODE(bureau_balance.STATUS))>]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feature_names[-15:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how featuretools stacks multiple primitives on top of each other. This one of the ideas behind Deep Feature Synthesis and automated feature engineering. Rather than having to do these groupings and aggregations by ourselves, Featuretools is able to handle it all using the framework (`entities`, `relationships`, and `primitives`) that we provide. We can also use Featuretools to expand on our domain knowledge. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building on Top of Domain Features\n",
    "\n",
    "Featuretools will automatically build thousands of features for us, but that does not mean we can't use our own knowledge to improve the predictive performance. Featuretools is able to augment our domain knowledge by stacking additional features on top of our domain knowledge based features. We identified and created numerous useful features in the manual feature engineering notebook, based on our own knowledge and that of thousands of data scientists working on this problem on Kaggle. Rather than getting only one domain knowledge feature, we can effectively get dozens or even hundreds. __Here we'll explain the options for using domain knowledge, but we'll stick with the simple implementation of Featuretools for comparison purposes.__\n",
    "\n",
    "For more information on any of these topics, see the [documentation](https://docs.featuretools.com/guides/tuning_dfs.html) or the other notebooks in this repository. \n",
    "\n",
    "### Seed Features \n",
    "\n",
    "Seed features are domain features that we make in the data that Featuretools is then able to build on top of. For example, we saw that the rate of a loan is an important feature because a higher rate loan is likely more risky. In Featuretools, we can encode the loan rate (both for the current loan and for previous loans) as a seed feature and Featuretools will build additional explanatory variables on this domain knowledge wherever possible. \n",
    "\n",
    "### Interesting Values\n",
    "\n",
    "Interesting values have a similar idea to seed features except they allow us to make conditional features. For example, we might want to find for each client the mean amount of previous loans that have been closed and the mean amount of previous loans that are still active. By specifying interesting values in `bureau` on the `CREDIT_ACTIVE` variable we can have Featuretools do exactly that! Carrying this out by hand would be extremely tedious and present numerous opportunities for errors.\n",
    "\n",
    "### Custom Primitives\n",
    "\n",
    "If we aren't satisfied with the primitives available to use in Featuretools, we can write our own functions to transform or aggregate the data. This is one of the most powerful capabilities in featuretools because it allows us to make very specific operations that can then be applied to multiple datasets. \n",
    "\n",
    "__In this notebook we concentrate on a basic implementation of Featuretools, but keep in mind these capabilities are available for optimizing the library and using domain knowledge!__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Selecting Primitives\n",
    "\n",
    "For our actual set of features, we will use a select group of primitives rather than just the defaults. This will generate over 1800 features to use for modeling. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Specify primitives\n",
    "agg_primitives =  [\"sum\", \"max\", \"min\", \"mean\", \"count\", \"percent_true\", \"num_unique\", \"mode\"]\n",
    "trans_primitives = ['percentile', 'and']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Built 1820 features\n"
     ]
    }
   ],
   "source": [
    "# Deep feature synthesis \n",
    "feature_names = ft.dfs(entityset=es, target_entity='app',\n",
    "                       agg_primitives = agg_primitives,\n",
    "                       trans_primitives = trans_primitives,\n",
    "                       n_jobs = -1, verbose = 1,\n",
    "                       features_only = True,\n",
    "                       max_depth = 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "ft.save_features(feature_names, 'input/features.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we save the features, we can then use them with `calculate_feature_matrix`. This is useful when we want to apply the same features across datasets (such as if we have separate trainig/testing)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run Full Deep Feature Synthesis\n",
    "\n",
    "If we are content with the features that will be built, we can run deep feature synthesis and create the feature matrix. The following call runs the full deep feature synthesis. This might take a long time depending on your machine. Featuretools does allow for parallel processing, but each core must be able to handle the entire entityset. \n",
    "\n",
    "__An actual run of this code was completed using Dask which can be seen in the [Featuretools on Dask notebook](https://github.com/Featuretools/Automated-Manual-Comparison/blob/master/Loan%20Repayment/notebooks/Featuretools%20on%20Dask.ipynb).__ The Dask code takes under 2 hours to run and is a great example of how we can use parallel processing to use our resouces in the most efficient manner."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total size of entityset: 11.62682 gb.\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "print('Total size of entityset: {:.5f} gb.'.format(sys.getsizeof(es) / 1e9))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total number of cpus detected: 4.\n",
      "Total size of system memory: 4.29497 gb.\n"
     ]
    }
   ],
   "source": [
    "import psutil\n",
    "\n",
    "print('Total number of cpus detected: {}.'.format(psutil.cpu_count()))\n",
    "print('Total size of system memory: {:.5f} gb.'.format(psutil.virtual_memory().total / 1e9))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# feature_matrix, feature_names = ft.dfs(entityset=es, target_entity='app',\n",
    "#                                        agg_primitives = agg_primitives,\n",
    "#                                        trans_primitives = trans_primitives,\n",
    "#                                        n_jobs = 1, verbose = 1, features_only = False,\n",
    "#                                        max_depth = 2, chunk_size = 100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "# feature_matrix.reset_index(inplace = True)\n",
    "# feature_matrix.to_csv('../input/feature_matrix.csv', index = False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To download the feature matrix, head to https://www.kaggle.com/willkoehrsen/home-credit-default-risk-feature-tools and select the `feature_matrix_article.csv`. There are several other versions of automatically engineered feature matrices available there as well. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Conclusions\n",
    "\n",
    "In this notebook, we saw how to implement automated feature engineering for a data science problem. __Automated feature engineering allows us to create thousands of new features from a set of related data tables, significantly increasing our efficiency as data scientists.__ Moreover, we can still use domain knowledge in our features and even augment our domain knowledge by building on top of our own hand-built features. The main takeaways are:\n",
    "\n",
    "* Automated feature engineering took 1 hour to implement compared to 10 hours for manual feature engineering\n",
    "* Automated feature engineering built thousands of features in a few lines of code compared to dozens of lines of code per feature for manual engineering.\n",
    "* Overall, performance of the automated features are comparable or better than those of the manual features (see the Results notebook)\n",
    "\n",
    "The benefits of automated feature engineering are significant and will considerably help us in our role as data scientists. It won't alleviate the need for data scientists, but rather will make us more efficient and build better predictive pipelines in less time. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Next Steps\n",
    "\n",
    "After creating a full set of features, we can apply feature selection and then proceed with modeling. To optimize the model for the features, we use random search for 100 iterations over a grid of hyperparamters. To see how to use Dask to run Featuretools in parallel, refer to the Featuretools Implementation with Dask notebook. For feature selection refer to the Feature Selection notebook. Final results are presented in the Results notebook."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p>\n",
    "    <img src=\"https://www.featurelabs.com/wp-content/uploads/2017/12/logo.png\" alt=\"Featuretools\" />\n",
    "</p>\n",
    "\n",
    "Featuretools was created by the developers at [Feature Labs](https://www.featurelabs.com/). If building impactful data science pipelines is important to you or your business, please [get in touch](https://www.featurelabs.com/contact)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
